/*
 * This is GPL v2 licensed code. See the file LICENSE in the
 * source distribution, or www.gnu.org/copyleft/gpl.html, for
 * more information.
 *
 * Copyright (C) by Håkan Lindqvist 2006, 2007, with the exception
 * the private classes whose copyright belong to Sun Microsystems, 
 * Inc. (See credit link).
 *
 * Credits goes to:
 * http://java.sun.com/developer/technicalArticles/Security/pwordmask
 */

package PolicyTool;

import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.Arrays;


/** 
 * This is a helper class for the PoolTool class, it reads
 * passwords from the standard input and tries <b>really</b>
 * hard to not show what is typed in.
 */
public class PasswordReader {
	private static String prompt;

	/** 
	 * Constructor. 
	 * 
	 * @param prompt What to show when requesting the password.
	 * @return The response from the user as a string.
	 * @throws IOException If something goes wrong.
	 */
	protected String getPassword(String prompt) throws IOException {
		this.prompt = prompt;
		char[] pw = PasswordField.getPassword(System.in);
		String ret = null;

		if (pw != null) {
			ret = new String(pw);
		} else {
			ret = new String("");
		}

		return ret;
	}


	/**
	 * This class attempts to erase characters echoed to the console.
	 */
	private static class MaskingThread extends Thread {
		private volatile boolean stop;
		private static final char echochar = '*';

		/**
		 *@param prompt The prompt displayed to the user
		 */
		public MaskingThread() {
			System.out.print(PasswordReader.prompt);
		}


		/**
		 * Begin masking until asked to stop.
		 */
		public void run() {

			int priority = Thread.currentThread().getPriority();
			Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

			try {
				stop = true;
				while(stop) {
					System.out.print("\010" + echochar);

					try {
						// attempt masking at this rate
						Thread.currentThread().sleep(1);
					}catch (InterruptedException iex) {
						Thread.currentThread().interrupt();
						return;
					}
				}
			} finally { // restore the original priority
				Thread.currentThread().setPriority(priority);
			}
		}

		/**
		 * Instruct the thread to stop masking.
		 */
		public void stopMasking() {
			this.stop = false;
		}
	}



	/**
	 * This class prompts the user for a password and attempts to mask input with "*"
	 */
	private static class PasswordField {

		/**
		 *@param input stream to be used (e.g. System.in)
		 *@return The password as entered by the user.
		 */
		public static char[] getPassword(InputStream in) throws IOException {
			/* Start the masking thread */
			MaskingThread maskingthread = new MaskingThread();
			Thread thread = new Thread(maskingthread);
			thread.start();

			char[] lineBuffer;
			char[] buf;
			int i;

			buf = lineBuffer = new char[128];

			int room = buf.length;
			int offset = 0;
			int c;

loop:   while (true) {
			switch (c = in.read()) {
				case -1:
				case '\n':
					break loop;

				case '\r':
					int c2 = in.read();
					if ((c2 != '\n') && (c2 != -1)) {
						if (!(in instanceof PushbackInputStream)) {
							in = new PushbackInputStream(in);
						}
						((PushbackInputStream)in).unread(c2);
					} else {
						break loop;
					}

				default:
					if (--room < 0) {
						buf = new char[offset + 128];
						room = buf.length - offset - 1;
						System.arraycopy(lineBuffer, 0, buf, 0, offset);
						Arrays.fill(lineBuffer, ' ');
						lineBuffer = buf;
					}
					buf[offset++] = (char) c;
					break;
			}
		}
		maskingthread.stopMasking();

		if (offset == 0) {
			return null;
		}

		char[] ret = new char[offset];
		System.arraycopy(buf, 0, ret, 0, offset);
		Arrays.fill(buf, ' ');
		return ret;
		}
	}
}
